
#include <ddk.h>
#include "drvassistant.h"
#include "nu_api.h"
#include <btlayer.h>
#include <btlapi.h>
//#include <zfile.h>
//
// bluetooth layer
//
static inBtTable s_intable;

static queueNode ** pp_qhead = NULL;
#define s_qhead     (* pp_qhead)
ECode ReInit_BT_FS();

class BTL : public CDeviceDriver
{
public:
    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf & buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf & inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();

    void Init();
    int DispatchBtlApi(outBtTable * nucleushandle,
                       int Function,
                       Byte* buff,
                       int size);

public:
    outBtTable * m_nucleushandle;
    DzEvent    * m_event_bt;
    char       * m_bt_cb_buff;
    DzEvent    * m_pei_event;
};

int BTL::DispatchBtlApi(outBtTable * nucleushandle, int Function, Byte* buff, int size)
{
    char * switchbuff = NULL;
    int len = 0;

    switch (Function)
    {
        case APP_INIT:              /*nucleushandle->APP_Init();*/ break;
        case APP_DEINIT:            /*nucleushandle->APP_DeInit();*/ break;
        case APP_PROCESSUSERACTION:

            if(((APP_ProcessUserAction_ARG*)buff)->plen && ((APP_ProcessUserAction_ARG*)buff)->databuff){
                len = *((APP_ProcessUserAction_ARG*)buff)->plen;

                assert(size <= (int)sizeof(APP_ProcessUserAction_ARG));
                switchbuff = (char*)malloc(len);
                if(switchbuff == NULL){
                    kprintf("malloc failed in function DispatchBtlApi.\n");
                    assert(0);
                    return -1;
                }
                memcpy(switchbuff, ((APP_ProcessUserAction_ARG*)buff)->databuff, len);
            }
            nucleushandle->APP_ProcessUserAction(
                ((APP_ProcessUserAction_ARG*)buff)->appMsgRcvId,
                ((APP_ProcessUserAction_ARG*)buff)->msg,
                switchbuff,
                &len);

            if(NULL != m_pei_event){
                WaitResult wrt;
                EventState state;
                m_pei_event->Wait(&wrt, &state);
            }else {
                kprintf("m_pei_event is NULL. \n");
            }

            if(((APP_ProcessUserAction_ARG*)buff)->plen && ((APP_ProcessUserAction_ARG*)buff)->databuff){
                memcpy(((APP_ProcessUserAction_ARG*)buff)->databuff, switchbuff, len);
                *((APP_ProcessUserAction_ARG*)buff)->plen = len;

                free(switchbuff);
            }
            break;
        default: kprintf("index out of boundary.\n");
            break;
    }
    return 0;
}

static int __cdecl CAS(int * destination, int ovalue, int nvalue)
{
    return InterlockedCompareExchange(destination, nvalue, ovalue);
}

ECode BTL::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    WaitResult wrt = WaitResult_TimedOut;
    queueNode * state = NULL;
    queueNode * qnode = NULL;
    queueNode * leftnode = NULL;

    if (pBuffer == NULL || !pBuffer->GetCapacity()) {
        return E_INVALID_ARGUMENT;
    }
//    kprintf("%s %d s_qhead %08x state %08x.\n", __FUNCTION__, __LINE__, s_qhead, state);
    while(s_qhead == NULL){
        m_event_bt->Wait(&wrt, (EventState *)&state);

        if(wrt != WaitResult_OK) {
            if (NULL != ppCompletionEvent) *ppCompletionEvent = NULL;
            return E_INTERRUPTED;
        }

        assert(state);

//        kprintf("%s %d s_qhead %08x state %08x.\n", __FUNCTION__, __LINE__, s_qhead, state);
//        kprintf("~ ");
//        DzSleep(30, NULL);
    }
    assert(s_qhead);

    //get qnode
    do{
       leftnode = s_qhead; qnode = leftnode->next;
        //only one node left
        if(qnode == NULL){
            if((int)leftnode == CAS((int*)&s_qhead, (int)leftnode, 0)){
                qnode = leftnode;
                break;
            }
            else{
                DzSleep(1, NULL);
            }
        }
        else{
            //find the last one
            while(qnode->next){
                leftnode = leftnode->next;
                qnode = leftnode->next;
            }

            if((int)qnode == CAS((int *)&leftnode->next, (int)qnode, 0)){
                break;
            }
            else{
                DzSleep(1, NULL);
            }
        }
    }while(1);

    //deal the qnode
    assert((sizeof(EventBuff) + qnode->buff->SizeofParam) <= (unsigned int)pBuffer->GetCapacity());
    pBuffer->Copy((Byte*)qnode->buff, sizeof(EventBuff) + qnode->buff->SizeofParam);

    if(m_nucleushandle->mem_free(qnode->buff)){
        kprintf("m_nucleushandle mem_free buff error.\n");
        return E_OUT_OF_MEMORY;
    }
    if(m_nucleushandle->mem_free(qnode)){
        kprintf("m_nucleushandle mem_free qnode error.\n");
        return E_OUT_OF_MEMORY;
    }

    if (NULL != ppCompletionEvent) *ppCompletionEvent = NULL;
    return NOERROR;
}

ECode BTL::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    Int32 numberOfBytesWritten;
    numberOfBytesWritten = DispatchBtlApi(m_nucleushandle, u64Offset, buffer.GetPayload(), buffer.GetUsed());
    if (NULL != pBytesWritten) *pBytesWritten = numberOfBytesWritten;

    if (NULL != ppCompletionEvent) *ppCompletionEvent = NULL;
    return NOERROR;
}

#if 0
typedef struct threadarg{
    DzEvent  * event;
    int      * running;
}threadarg;

Int32 fileThread(void *arg)
{
    int * running = ((threadarg *)arg)->running;
    WaitResult wrt;
    FileArg *filearg;
    DzEvent  * event = ((threadarg *)arg)->event;

    *running = 1;

    while(*running){

        event->Wait(&wrt, (EventState *)&filearg);
        if(wrt != WaitResult_OK) {
            *running = 0;
            kprintf("fileThread wait error.\n");
            return -1;
        }
        switch(filearg->function)
        {
            case ZOPEN:
                *filearg->pret = zopen((const char*)filearg->arg1,
                        (int)filearg->arg2);
                break;
            case ZREAD:
                *filearg->pret = zread(filearg->arg1, (void*)filearg->arg2,
                        filearg->arg3);
                break;
            case ZWRITE:
                *filearg->pret = zwrite(filearg->arg1, (void*)filearg->arg2,
                        filearg->arg3);
                break;
            case ZSEEK:
                *filearg->pret = zseek(filearg->arg1, filearg->arg2,
                        filearg->arg3);
               break;
            case ZCLOSE:
                *filearg->pret = zclose(filearg->arg1);
                break;
            default:
                kprintf("forbidden territory.\n");
                break;
        }

        nu_set_event(FILE_EVENT);
    }

    return 0;
}
#endif

static int btdrvstate = 0;
static int drvavailable()
{
    return btdrvstate;
}

ECode BTL::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
//    threadarg arg;
    switch (nControlCode) {

        case 0:{//alloc resources
            m_event_bt = new DzEvent(/*FALSE, UNSIGNALED*/);
            if(NULL == m_event_bt) return E_OUT_OF_MEMORY;

            m_pei_event = new DzEvent(/*FALSE, UNSIGNALED*/);
            if(NULL == m_pei_event) return E_OUT_OF_MEMORY;


            m_bt_cb_buff = (char*)malloc(MEMORYMAXSIZE);
            if(NULL == m_bt_cb_buff){
                kprintf("%s malloc error.\n", __FUNCTION__);
                return E_OUT_OF_MEMORY;
            }

            s_intable.CAS = CAS;
            s_intable.event_bt = m_event_bt;
            s_intable.event_pei_bt = m_pei_event;
            s_intable.ela_btstate = drvavailable;

            m_nucleushandle = (outBtTable *)nu_btl(&s_intable);

            if(NULL == m_nucleushandle) return E_INVALID_OPERATION;

            if(m_nucleushandle->mem_init(m_bt_cb_buff, MEMORYMAXSIZE)){
                kprintf("memory init error.\n");
                return E_OUT_OF_MEMORY;
            }
            pp_qhead = (queueNode**)m_nucleushandle->phead;
            kprintf("pp_qhead %08x s_qhead %08x.\n", pp_qhead, s_qhead);

            btdrvstate = 1;
            break;
        }
		case 1:{//release resources
            btdrvstate = 0;
    		if(m_event_bt) delete m_event_bt;
    		m_event_bt = NULL;

    		if(m_nucleushandle->mem_deinit()){
        		kprintf("memory deinit error.\n");
    		}

    		if(m_bt_cb_buff) free(m_bt_cb_buff);
    		m_bt_cb_buff = NULL;

    		if(m_pei_event) delete m_pei_event;
    		m_pei_event = NULL;
    		break;
		}
		case 2:{//reinit fs after reload disk
		    ReInit_BT_FS();
		    break;
		}
		default:
		    break;


    }

    return NOERROR;
}

void BTL::Dispose()
{
    delete this;
}

void BTL::Init()
{
    m_bt_cb_buff = NULL;
}


EXTERN IDeviceDriver * CDECL CreateBTL(uint_t uDeviceNo, void *pvParameter)
{

    BTL *pBTL = new BTL();
    if (NULL == pBTL){
        kprintf("Create BlueTooth Failed.\n");
        return NULL;
    }
    pBTL->AddRef();
    kprintf("Create BlueTooth OK.\n");
    return pBTL;
}
